package net.miranti;

/*
 * Copyright 2008 Pavel Jbanov.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;

import net.miranti.dao.ResourceLocalDaoUtils;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;

/**
 * GuiceRemoteServiceServlet is a wrapper servlet for GWT RPC utilizing Guice
 * for managing RemoteService implementation object's lifecycle.
 */
@Singleton
public class GuiceRemoteServiceServlet extends RemoteServiceServlet {
	@Inject
	private Injector injector;

	@Override
	public String processCall(String payload) throws SerializationException {
		RPCRequest rpcRequest = RPC.decodeRequest(payload, null, this);
		SerializationPolicy policy = rpcRequest.getSerializationPolicy();
		try {
			return RPC.invokeAndEncodeResponse(getService(payload), rpcRequest
					.getMethod(), rpcRequest.getParameters(), policy);
		} catch (Throwable ex) {
			ResourceLocalDaoUtils.markTransactionForRollback();
			// this will force default Servlet renderer generate a Stack Trace
			// and send it to GWT
			throw new Error(ex);
		}
	}

	private RemoteService getService(String payload) {
		try {
			ClassLoader classLoader = Thread.currentThread()
					.getContextClassLoader();
			ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(
					classLoader, this);
			streamReader.prepareToRead(payload);
			Class<RemoteService> c = (Class<RemoteService>) Class
					.forName(streamReader.readString());
			return injector.getInstance(c);
		} catch (Exception e) {
			throw new Error(e);
		}
	}

	@Override
	protected void doUnexpectedFailure(Throwable e) {
		// replace default RemoteServiceServlet error handling
		ServletContext servletContext = getThreadLocalRequest().getSession()
				.getServletContext();
		servletContext.log("Exception while dispatching incoming RPC call", e);

		HttpServletResponse response = getThreadLocalResponse();
		// Send Exception message with 500 status.
		try {
			response.setContentType("text/plain");
			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			try {
				response.getOutputStream().write(e.getMessage().getBytes("UTF-8"));
			} catch (IllegalStateException ex) {
				response.getWriter().write("response.getWriter() was previously used");
			}
		} catch (IOException ex) {
			String msg = "respondWithUnexpectedFailure failed while sending the previous failure to the client"; 
			servletContext.log(msg, ex);
		}
	}
}